
#include "../server.hpp"

#include <fstream>
#include <regex>
#include <sys/stat.h>

#define DEFAULT_TIMEOUT 10


unordered_map<int, string> _statu_msg = {
    {100, "Continue"}, {101, "Switching Protocol"}, {102, "Processing"}, {103, "Early Hints"},
    {200, "OK"}, {201, "Created"}, {202, "Accepted"}, {203, "Non-Authoritative Information"}, {204, "No Content"},
    {205, "Reset Content"}, {206, "Partial Content"}, {207, "Multi-Status"}, {208, "Already Reported"}, {226, "IM Used"},
    {300, "Multiple Choice"}, {301, "Moved Permanently"}, {302, "Found"}, {303, "See Other"}, {304, "Not Modified"},
    {305, "Use Proxy"},{306, "unused"},{307, "Temporary Redirect"},{308, "Permanent Redirect"},
    {400, "Bad Request"},{401, "Unauthorized"},{402, "Payment Required"},{403, "Forbidden"},{404, "Not Found"},{405, "Method Not Allowed"},
    {406, "Not Acceptable"},{407, "Proxy Authentication Required"},{408, "Request Timeout"},{409, "Conflict"},{410, "Gone"},
    {411, "Length Required"},{412, "Precondition Failed"},{413, "Payload Too Large"},{414, "URI Too Long"},{415, "Unsupported Media Type"},
    {416, "Range Not Satisfiable"},{417, "Expectation Failed"},{418, "I'm a teapot"},{421, "Misdirected Request"},{422, "Unprocessable Entity"},
    {423, "Locked"},{424, "Failed Dependency"},{425, "Too Early"},{426, "Upgrade Required"},{428, "Precondition Required"},
    {429, "Too Many Requests"},{431, "Request Header Fields Too Large"},{451, "Unavailable For Legal Reasons"},
    {501, "Not Implemented"},{502, "Bad Gateway"},{503, "Service Unavailable"},{504, "Gateway Timeout"},{505, "HTTP Version Not Supported"},
    {506, "Variant Also Negotiates"},{507, "Insufficient Storage"},{508, "Loop Detected"},{510, "Not Extended"},{511, "Network Authentication Required"}
};
unordered_map<string, string> _mime_msg = 
{
    {".aac", "audio/aac"}, {".abw", "application/x-abiword"}, {".arc", "application/x-freearc"}, {".avi", "video/x-msvideo"}, {".azw", "application/vnd.amazon.ebook"},
    {".bin", "application/octet-stream"}, {".bmp", "image/bmp"}, {".bz", "application/x-bzip"}, {".bz2", "application/x-bzip2"},
    {".csh", "application/x-csh"}, {".css", "text/css"}, {".csv", "text/csv"}, 
    {".doc", "application/msword"}, {".docx", "application/vnd.openxmlformatsofficedocument.wordprocessingml.document"},
    {".eot", "application/vnd.ms-fontobject"}, {".epub", "application/epub+zip"},
    {".gif", "image/gif"},
    {".htm", "text/html"}, {".html", "text/html"},
    {".ico", "image/vnd.microsoft.icon"}, {".ics", "text/calendar"},
    {".jar", "application/java-archive"}, {".jpeg", "image/jpeg"}, {".jpg", "image/jpeg"}, {".js", "text/javascript"}, {".json", "application/json"}, {".jsonld", "application/ld+json"},
    {".mid", "audio/midi"}, {".midi", "audio/x-midi"}, {".mjs", "text/javascript"}, {".mp3", "audio/mpeg"}, {".mpeg", "video/mpeg"}, {".mpkg", "application/vnd.apple.installer+xml"},
    {".odp", "application/vnd.oasis.opendocument.presentation"}, {".ods", "application/vnd.oasis.opendocument.spreadsheet"}, {".odt", "application/vnd.oasis.opendocument.text"},
    {".oga", "audio/ogg"}, {".ogv", "video/ogg"}, {".ogx", "application/ogg"}, {".otf", "font/otf"},
    {".png", "image/png"}, {".pdf", "application/pdf"}, {".ppt", "application/vnd.ms-powerpoint"}, {".pptx", "application/vnd.openxmlformatsofficedocument.presentationml.presentation"},
    {".rar", "application/x-rar-compressed"}, {".rtf", "application/rtf"},
    {".sh", "application/x-sh"}, {".svg", "image/svg+xml"}, {".swf", "application/x-shockwave-flash"},
    {".tar", "application/x-tar"}, {".tif", "image/tiff"}, {".tiff", "image/tiff"}, {".ttf", "font/ttf"}, {".txt", "text/plain"},
    {".vsd", "application/vnd.visio"},
    {".wav", "audio/wav"}, {".weba", "audio/webm"}, {".webm", "video/webm"}, {".webp", "image/webp"}, {".woff", "font/woff"}, {".woff2", "font/woff2"},
    {".xhtml", "application/xhtml+xml"}, {".xls", "application/vnd.ms-excel"}, {".xlsx", "application/vnd.openxmlformatsofficedocument.spreadsheetml.sheet"}, {".xml", "application/xml"}, {".xul", "application/vnd.mozilla.xul+xml"},
    {".zip", "application/zip"}, {".3gp", "video/3gpp"}, {".3g2", "video/3gpp2"}, {".7z", "application/x-7z-compressed"}
};


class Util
{
public:
    // 字符串分割
    static size_t Split(const string& src, const string& sep, vector<string>* array)
    {
        size_t begin = 0;
        while(begin < src.size())
        {
            size_t pos = src.find(sep, begin);
            if(pos == begin) 
            {
                begin = pos + sep.size();
                continue;
            }
            if(pos == string::npos)
            {
                array->push_back(src.substr(begin));
                break;
            }
            array->push_back(src.substr(begin,pos - begin));
            begin = pos + sep.size();
        }

        return array->size();
    }
    //  读取文件内容
    static bool ReadFile(const string& filename, string* buf)
    {
        ifstream ifs(filename, ios::binary);
        if(ifs.is_open() == false)
        {
            ERR_LOG("open %s file failed",filename.c_str());
            return false;
        }
        // 获取文件大小
        size_t fsize = 0;
        ifs.seekg(0, ifs.end);  //偏移到文件末尾
        fsize = ifs.tellg();    //获取输入序列中的位置，获取偏移位置就获取到了文件大小
        ifs.seekg(0,ifs.beg);   //在把偏移位置回到文件开始

        buf->resize(fsize);
        ifs.read(&(*buf)[0], fsize);
        if(ifs.good() == false)
        {
            ERR_LOG("%s file read failed",filename.c_str());
            ifs.close();
            return false;
        }
        ifs.close();
        return true;
    }
    // 向文件写入数据
    static bool WriteFile(const string& filename, const string& buf)
    {
        ofstream ofs(filename, ios::binary | ios::trunc);   //trunc丢弃原有内容
        if(ofs.is_open() == false)
        {
            ERR_LOG("open %s file failed",filename.c_str());
            return false;
        }        
        ofs.write(&buf[0], buf.size());
        if(ofs.good() == false)
        {
            ERR_LOG("%s file write failed",filename.c_str());
            ofs.close();
            return false;
        }
        ofs.close();
        return true;
    }
    // URL编码 避免url中资源路径与查询字符串中的特殊字符与HTTP请求中的特殊字符产生歧义
    // 编码格式：将特殊字符的ASIIC码值，转换为两个16进制字符，前缀% C++ -> C%2b%2b
    // 不编码的特殊字符：RFC3986文档规定 . - _ ~ 字母 数字 属于绝对不编码字符
    // RFC3986文档规定，编码格式为 %HH
    // W3C标准中规定，查询字符串中的空格，编码转换为 + 
    static string UrlEncode(const string& url, bool convert_space_to_plus)
    {
        string res = "";
        for(auto c:url)
        {
            if(c == '.' || c == '-' || c == '_' || c == '~' || isalnum(c))
                res += c;
            else if(c == ' ' && true == convert_space_to_plus)
                res += '+';
            else
            {
                char buf[4] = {0};
                snprintf(buf, 4, "%%%02X",c);
                res += buf;
            }
        }
        return res;
    }
    static char HEXToi(char c)
    {
        if(c >= '0' && c <= '9')
            return c - '0';
        else if(c >= 'a' && c <= 'z')
            return c - 'a' +10;
        else if(c >= 'A' && c <= 'Z')
            return c - 'A'+10;
        return -1;
    }
    // URL解码
    static string UrlDecode(const string& url, bool convert_space_to_plus)
    {
        string res = "";
        for(int i = 0; i < url.size(); ++i)
        {
            if(url[i] == '%' && (i+2) < url.size())
            {        
                char v1 = Util::HEXToi(url[i+1]);
                char v2 = Util::HEXToi(url[i+2]);
                char v = (v1 << 4) + v2;         // 左移4位 相当于*16

                res += v;
                i += 2;
            }
            else if(url[i] == '+' && true == convert_space_to_plus)
                res += ' ';
            else
                res += url[i];
        }
        return res;
    }
    // 响应状态码描述信息获取
    static string StatuDesc(int statu)
    {
        
        auto it = _statu_msg.find(statu);
        if(it != _statu_msg.end())
            return it->second;
        
        return "Unknow";
    }
    // 根据文件后缀名获取文件mime
    static string ExtMime(const string& filename)
    {

        // 获取文件后缀名 a.txt  dsf.jpg
        auto pos = filename.find_last_of(".");
        if( pos == string ::npos)
            return "application/octet-stream";  //找不到后缀，说明是一个二进制文件
        
        string ext = filename.substr(pos);
        auto it = _mime_msg.find(ext);
        if( it == _mime_msg.end())
            return "application/octet-stream";

        return it->second;
    }
    // 判断一个文件是否是一个目录
    static bool IsDirectory(const string& filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if(ret < 0)
            return false;
        return S_ISDIR(st.st_mode);
    }
    // 判断一个文件是否是普通文件
    static bool IsRegular(const string& filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if(ret < 0)
            return false;
        return S_ISREG(st.st_mode);
    }
    // http请求的资源路径有效性判断
    static bool validPath(const string& path)
    {
        vector<string> subdir;
        Split(path, "/", &subdir);
        
        int level = 0;
        for(auto &dir:subdir)
        {
            if(dir == "..")
                if(--level < 0) return false;
            else
                ++level;
        }
        return true;
    }
};

class HttpRequest
{
public:
    string _method;         // 请求方法
    string _path;           // 资源路径
    string _version;        // 协议版本
    string _body;           // 请求正文
    smatch _matches;        // 资源路径的正则提取数据
    unordered_map<string, string> _headers; // 头部字段
    unordered_map<string, string> _params;  // 查询字符串

public:
    HttpRequest():_version("HTTP/1.1"){}
    // 重置请求
    void ReSet()
    {
        _method.clear();
        _path.clear();
        _version = "HTTP/1.1";
        _body.clear();
        smatch matches;
        _matches.swap(matches);
        _headers.clear();
        _params.clear();
    }
    // 插入头部字段
    void SetHeader(const string& key, const string& val)
    {
        _headers.insert(make_pair(key, val));
    }
    // 判断是否存在指定头部字段
    bool HasHeader(const string& key) const
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
            return false;
        return true;
    }
    // 获取指定头部字段的值
    string GetHeader(const string& key) const
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
            return "";
        return it->second;
    }
    // 插入查询字符串
    void SetParam(const string& key, const string& val)
    {
        _params.insert(make_pair(key, val));
    }
    // 判断是否存在指定的查询字符串
    bool HasParam(const string& key) const
    {
        auto it = _params.find(key);
        if(it == _params.end())
            return false;
        return true;
    }
    // 获取指定的查询字符串
    string GetParam(const string& key) const
    {
        auto it = _params.find(key);
        if(it == _params.end())
            return "";
        return it->second;
    }
    // 获取正文长度
    size_t contentLength() const
    {
        // 头部字段有一个： Content-length: 1234\r\n
        bool ret = HasHeader("Content-Length");
        if(ret == false) return 0;
        string size = GetHeader("Content-Length");
        return stol(size);
    }
    // 判断是否是短连接
    bool IsClose() const
    {
        // 没有Connection字段或者有Connection字段但是值是close，这是短连接，值是keep-alive 证明是长链接
        if(HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive")
            return false;
        return true;
    }
};

class HttpResponse
{
public:
    int _statu;                 
    bool _redirect_flag;
    string _body;
    string _redirect_url;
    unordered_map<string, string> _headers;
public:
    HttpResponse():_statu(200),_redirect_flag(false){}
    HttpResponse(int statu):_statu(statu),_redirect_flag(false){}
    void ReSet()
    {
        _statu = 200;
        _redirect_flag = false;
        _body.clear();
        _redirect_url.clear();
        _headers.clear();
    }
    void SetHeader(const string& key, const string& val)
    {
        _headers.insert(make_pair(key, val));
    }
    bool HasHeader(const string& key)
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
            return false;
        return true;
    }
    string GetHeader(const string& key)
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
            return "";
        return it->second;
    }
    // 设置正文
    void SetContent(const string& body, const string& type)// = "text/html"
    {
        _body = body;
        SetHeader("Content-Type", type);
    }
    // 设置重定向
    void SetRedirect(const string& url, int statu = 302)
    {
        _statu = statu;
        _redirect_flag = true;
        _redirect_url = url;
    }
    bool IsClose()
    {
        if(HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive")
            return false;
        return true;
    }
};

typedef enum{ 
    RECV_HTTP_ERROR, RECV_HTTP_LINE, 
    RECV_HTTP_HEAD, RECV_HTTP_BODY, RECV_HTTP_OVER 
    }HttpRecvStatu;

#define MAX_LINE 8192

class HttpContext
{
private:
    int _resp_statu;            //响应状态码
    HttpRecvStatu _recv_statu;  //接收及解析的阶段状态
    HttpRequest _request;       //已经解析到的请求信息
private:
    bool RecvHttpLine(Buffer* buf)
    {
        if(_recv_statu != RECV_HTTP_LINE) return false;
        // 获取一行数据
        string line = buf->GetLineAndPop(); //?
        // 要考虑的因素：缓冲区的数据不足一行，或者缓冲区的数据超大
        if(line.size() == 0)
        {   
            // 缓冲区中的数据不足一行时，需要判断缓冲区中的可读数据大小，如果很大了都不足一行是有问题的
            if(buf->ReadableSize() > MAX_LINE)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _resp_statu = 414;  //URI TO LONG
                return false;
            }
            else
                return true;    //缓冲区中的数据不足一行，但是数据也不多，那么就等等
        }
        if(line.size() > MAX_LINE)
        {
            _recv_statu = RECV_HTTP_ERROR;
            _resp_statu = 414;      //URI TO LONG
            return false;
        }

        return ParseHttpLine(line);
    }
    bool ParseHttpLine(const string& line)
    {
        smatch matches;
        regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?" , regex::icase);
        bool ret = regex_match(line, matches, e);
        if(ret == false)
        {
            _recv_statu = RECV_HTTP_ERROR;
            _resp_statu = 400;     // BAD REQUEST
            return false;            
        }
        // 0: GET /bitekeji/login?user=xiaoli&password=123456 HTTP/1.1\r\n;
        // 1: GET 
        // 2: /bitekeji/login 
        // 3: user=xiaoli&password=123456 
        // 4: HTTP/1.1
        _request._method = matches[1];                      //请求方法获取
        transform(_request._method.begin(), _request._method.end(), _request._method.begin(), ::toupper);
        _request._path = Util::UrlDecode(matches[2],false); //资源路径获取，需要进行URL解码，但是不需要解空格
        _request._version = matches[4];                     //协议版本获取
        // 查询字符串的获取与处理
        string src = matches[3];            
        vector<string> param;
        // 查询字符串的格式 key=val&key=val....，先以&进行字符串分割，得到KV式的子串
        Util::Split(src, "&", &param);
        // 针对子串，以=进行分割，得到key和val，将 key和val进行URL解码，这里需要 "+ -> 空格" 而后
        for(auto &str:param)
        {
            size_t pos = str.find("=");
            if(pos == string::npos)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _resp_statu = 400;     // BAD REQUEST     
                return false;    
            }
            string key = Util::UrlDecode(str.substr(0,pos),true);
            string val = Util::UrlDecode(str.substr(pos+1),true);
            _request.SetParam(key, val);
        }
        // 首行处理完毕，进入头部字段获取阶段
        _recv_statu = RECV_HTTP_HEAD;
        return true;
    }
    bool RecvHttpHead(Buffer* buf)
    {
        if(_recv_statu != RECV_HTTP_HEAD) return false;
        // 一行一行的取出数据，直到遇到空行为止，头部字段格式： key: val\r\nkey: val\r\n.....
        while(1)
        {
            // 获取一行数据
            string line = buf->GetLineAndPop(); //?
            // 要考虑的因素：缓冲区的数据不足一行，或者缓冲区的数据超大
            if(line.size() == 0)
            {   
                // 缓冲区中的数据不足一行时，需要判断缓冲区中的可读数据大小，如果很大了都不足一行是有问题的
                if(buf->ReadableSize() > MAX_LINE)
                {
                    _recv_statu = RECV_HTTP_ERROR;
                    _resp_statu = 414;  //URI TO LONG
                    return false;
                }
                else
                    return true;    //缓冲区中的数据不足一行，但是数据也不多，那么就等等
            }
            if(line.size() > MAX_LINE)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _resp_statu = 414;      //URI TO LONG
                return false;
            }
            if(line == "\n" || line == "\r\n")
                break;
            
            if(ParseHttpHead(line) == false)
                return false;
        }
        // 头部处理完毕，进入正文获取阶段
        _recv_statu = RECV_HTTP_BODY;
        return true;
    }
    bool ParseHttpHead(string& line)
    {
        // key: val\r\n 
        // 要将字符串末尾的\r\n去掉
        if(line.back() == '\n') line.pop_back();    //字符串末尾是\n就去掉
        if(line.back() == '\r') line.pop_back();    //字符串末尾是\r就去掉
        size_t pos = line.find(": ");
        if( pos == string::npos)
        {
            _recv_statu = RECV_HTTP_ERROR;
            _resp_statu = 400;     // BAD REQUEST     
            return false;   
        }
        string key = line.substr(0,pos);
        string val = line.substr(pos+2);
        _request.SetHeader(key,val);
        return true;
    }
    bool RecvHttpBody(Buffer* buf)
    {
        if(_recv_statu != RECV_HTTP_BODY) return false;
        // 获取正文长度
        size_t content_len = _request.contentLength();
        if(content_len == 0)    //正文那长度==0 ，表示没有正文
        {
            _recv_statu = RECV_HTTP_OVER;   
            return true;
        }
        // 当前已经接收了多少正文，就是看_request._body里有多少数据
        size_t real_len = content_len - _request._body.size();
        // 接收正文放到body中，但是也要考虑当前缓冲区中的数据，是否是全部的正文
            // 缓冲区中的数据，包含了当前请求的所有正文，则取出数据
        if(buf->ReadableSize() >= real_len)
        {
            _request._body.append(buf->ReadPosition(), real_len);
            buf->MoveReadOffset(real_len);
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }
            //缓冲区中的数据，不够正文的总长度，取出缓冲区中的数据，而后等待新数据
        _request._body.append(buf->ReadPosition(), buf->ReadableSize());
        buf->MoveReadOffset(buf->ReadableSize());
        return true;
        
    }
public:
    HttpContext():_resp_statu(200), _recv_statu(RECV_HTTP_LINE) {}
    void ReSet()
    {
        _resp_statu = 200;
        _recv_statu = RECV_HTTP_LINE;
        _request.ReSet();
    }
    int RespStatu() { return _resp_statu; }
    HttpRecvStatu RecvStatu() { return _recv_statu; }
    HttpRequest& Request() { return _request; }
    // 接收并解析Http请求
    void RecvHttpRequest(Buffer* buf)
    {
        // 不同的状态，做不同的事情，这里不需要break。
        // 因为处理完请求行后，要接着往下处理头部字段，而不是退出等待新数据
        switch(_recv_statu)
        {
            case RECV_HTTP_LINE : RecvHttpLine(buf);
            case RECV_HTTP_HEAD : RecvHttpHead(buf);
            case RECV_HTTP_BODY : RecvHttpBody(buf);
        }
    }

};


class HttpServer
{
private:
    using Handler = function<void(const HttpRequest&, HttpResponse*)>;
    using Handlers = vector<pair<regex, Handler>>;
    Handlers _get_route;
    Handlers _post_route;
    Handlers _put_route;
    Handlers _delete_route;
    string _basedir;        // 静态资源根目录
    TcpServer _server;
private:
    void ErrorHandler(const HttpRequest& req, HttpResponse* rsp)
    {

        // 1. 组织一个错误展示页面
        string body = "<html><head><meta http-equiv='Content-Type' content='text/html;charset=utf-8'></head>";
        body = body + "<body><h1>" + to_string(rsp->_statu) + " " + Util::StatuDesc(rsp->_statu) + "</h1></body></html>";
        // 2. 将页面数据当做响应正文，放入rsp中
        rsp->SetContent(body, "text/html");
    }
    //将HttpResponse中的要素按照HTTP协议格式进行组织，发送
    void WriteResponse(const PtrConnection& conn, const HttpRequest& req, HttpResponse& rsp)
    {
        // 1. 先完善头部字段
        if(req.IsClose() == true)
        {
            rsp.SetHeader("Connection", "close");
        }
        else
        {
            rsp.SetHeader("Connection", "keep-alive");
        }
        if(rsp._body.empty() == false && rsp.HasHeader("Content-Length") == false)
            rsp.SetHeader("Content-Length", to_string(rsp._body.size()));
        if(rsp._body.empty() == false && rsp.HasHeader("Content-Type") == false)
            rsp.SetHeader("Content-Type", "application/octet-stream");
        if(rsp._redirect_flag == true)
            rsp.SetHeader("Location", rsp._redirect_url);

        // 2. 将rsp中的要素按照HTTP协议格式进行组织
        stringstream rsp_str;
        rsp_str << req._version << " " << to_string(rsp._statu) << " " << Util::StatuDesc(rsp._statu) << "\r\n";
        for(auto& head:rsp._headers)
        {
            rsp_str << head.first << ": " << head.second << "\r\n";
        }
        rsp_str << "\r\n";
        rsp_str << rsp._body;
        
        // 3. 发送数据
        conn->Send(rsp_str.str().c_str(), rsp_str.str().size());
    }
    // 是否是一个静态资源请求
    bool IsFileHandler(const HttpRequest& req)
    {

        // 1. 必须设置了静态资源根目录
        if(_basedir.empty())
            return false;
        // 2. 请求方法必须是 GET / HEAD 请求方法
        if(req._method != "GET" && req._method != "HEAD")
            return false;
        // 3. 请求的资源路径必须是一个合法路径
        if(Util::validPath(req._path) == false)
            return false;
        // 4. 请求的资源必须存在,且是一个普通文件
        // 前缀的相对根目录，也就是将请求路径转化为实际存在的路径 /image/a.png -> ./wwwroot/image/a.png
        string req_path = _basedir + req._path;     //为了避免直接修改请求的资源路径，定义一个临时对象
        // 有一种请求比较特殊：是一个目录 / 或/image/  ，这种情况需要再后面加一个index.html
        if(req._path.back() == '/')
            req_path += "index.html";
        if(Util::IsRegular(req_path) == false)
            return false; 
        return true;
    }
    //静态资源的请求处理--将静态资源的文件数据读取出来，放到rsp的body中并设置mime
    void FileHandler(const HttpRequest& req, HttpResponse* rsp)
    {

        string req_path = _basedir + req._path;     //为了避免直接修改请求的资源路径，定义一个临时对象
        if(req._path.back() == '/')
            req_path += "index.html";
        bool ret = Util::ReadFile(req_path, &rsp->_body);
        if(ret == false)
            return ;
        string mime = Util::ExtMime(req_path);
        rsp->SetHeader("Content-Type", mime);
        return ;
    }     
    //功能性请求的分类处理
    void DisPatcher( HttpRequest& req, HttpResponse* rsp, Handlers& handlers)
    {
        // 在对应请求方法的路由表中，查找是否含有对应资源请求的处理函数，有则调用，没有返回404
        // 思想： 路由表存储的是键值对 --- 正则表达式 & 处理函数
        // 使用正则表达式，对请求的资源路径进行正则匹配，匹配成功就是用对应函数进行处理
        // /numbers/(\d+)         /numbers/12345
        for(auto& handler:handlers)
        {
            const regex& re = handler.first;
            const Handler& functor = handler.second;
            bool ret = regex_match(req._path, req._matches, re);
            if(ret == false)
                continue;
            
            return functor(req, rsp);
        }
        rsp->_statu = 404;
    }      
    void Route(HttpRequest& req, HttpResponse* rsp )
    {
        // 对请求进行分析，是静态资源请求，还是功能性请求
        // 静态资源请求，则进行静态资源的处理
        // 功能性请求，则需要通过几个请求路由表来确定是否有处理函数
        // 既不是静态资源请求，也没有设置对应的功能性请求处理函数，就返回405
        if(IsFileHandler(req) == true)
        {
            // 是一个静态资源请求，则进行静态资源请求的处理
            return FileHandler(req, rsp);
        }
        if(req._method == "GET" || req._method == "HEAD")
            return DisPatcher(req, rsp, _get_route);
        else if(req._method == "POST")
            return DisPatcher(req, rsp, _post_route);
        else if(req._method == "PUT")
            return DisPatcher(req, rsp, _put_route);
        else if(req._method == "DELETE")
            return DisPatcher(req, rsp, _delete_route);

        rsp->_statu = 405;  //Method Not Allowed
    }
    //设置上下文
    void OnConnected(const PtrConnection& conn)
    {
        conn->SetContext(HttpContext());
        DBG_LOG("new connetcion %p",conn.get());
    }
    //缓冲区解析+处理
    void OnMessage(const PtrConnection& conn, Buffer* buf)
    {
        while(buf->ReadableSize() > 0)
        {
            // 1.获取上下文
            HttpContext* context = conn->GetContext()->get<HttpContext>();
            // 2.通过上下文对缓冲区数据进行解析，得到HttpRequest对象
            context->RecvHttpRequest(buf);
            HttpRequest& req = context->Request();
            HttpResponse rsp(context->RespStatu());
            //    2.1 如果缓冲区信息解析出错，就直接回复出错响应
            //    2.2 如果解析正常，且请求已经获取完毕，才开始进行处理
            if(context->RespStatu() >= 400)
            {
                // 进行错误响应关闭连接
                ErrorHandler(req, &rsp);         //填充一个错误显示页面数据到rsp
                WriteResponse(conn, req, rsp); //组织响应发送给客户端
                context->ReSet();   //出错了要重置上下文
                buf->MoveReadOffset(buf->ReadableSize());//出错了就把缓冲区里的数据清空
                conn->ShutDown();               //关闭连接
                return ;
            }
            if(context->RecvStatu() != RECV_HTTP_OVER)
                return;     //当前请求还没有接收完整，先退出，等新数据到来再处理
            // 3.请求路由+业务处理
            Route(req, &rsp);
            // 4.对HttpResponse进行组织发送
            WriteResponse(conn, req, rsp);
            // 重置上下文
            context->ReSet();
            // 5.根据长短连接判断是否关闭连接或者继续处理
            if(rsp.IsClose() == true) conn->ShutDown(); //短连接直接关闭
        }
    }       
public:
    HttpServer(uint16_t port, int timeout = DEFAULT_TIMEOUT):_server(port)
    {
        _server.EnableInactiveRelease(timeout);
        _server.SetConnectedCallback(bind(&HttpServer::OnConnected, this, placeholders::_1));
        _server.SetMessageCallback(bind(&HttpServer::OnMessage, this, placeholders::_1, placeholders::_2));
    }
    void SetBaseDir(const string& path) 
    {  
        assert(Util::IsDirectory(path) == true);
        _basedir = path;
    }
    // 设置 请求(请求的正则表达式)与处理函数的映射关系
    void Get(const string& pattern, const Handler& handler)
    {
        _get_route.push_back(make_pair(regex(pattern), handler));
    }
    void Post(const string& pattern, const Handler& handler)
    {
        _post_route.push_back(make_pair(regex(pattern), handler));
    }
    void Put(const string& pattern, const Handler& handler)
    {
        _put_route.push_back(make_pair(regex(pattern), handler));
    }
    void Delete(const string& pattern, const Handler& handler)
    {
        _delete_route.push_back(make_pair(regex(pattern), handler));
    }
    void SetThreadCount(int count)
    {
        _server.SetThreadCount(count);
    }
    void StartListen()
    {
        _server.StartServer();
    }
};